<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>

  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta name="Generator" content="Emacs, Lisp, Python, Linux">
    <meta name="Keywords" content=",GNU,Linux,Lisp,FreeBSD,C,Emacs,Python,Java,JOY工作室">
    <meta name="Description" content="无很的主页，热爱GNU/Linux，GNU/Lisp的忠实粉丝">
    <meta name="Author" content="无很">
    <meta name="Robots" content="index">
    <link rel="stylesheet" type="text/css" charset="utf-8" media="all" href="../style.css">
    <title>Lisp的求值与赋值
	</title>
  </head>
  
  <body>

    <div class="menu">
      <a title="无很主页" href="http://www.lisperblog.com">Lisperblog</a>
      <a title="无很的主页" href="/index.html">主页</a>
      <a href="/Lisp/index.html">Lisp</a>
	  <a href="/Python/index.html">Python</a>
	  <a href="/Java/index.html">Java</a>
	  <a href="/Linux/index.html">Linux</a>
	  <a href="/FreeBSD/index.html">FreeBSD</a>
      <a title="关于我，联系信息" href="/About.html">联系</a>
    </div>

    <div id="header">
      <h1 >Lisp的求值与赋值</h1>
      <!--<h2><a href="mailto:wuhen86@gmail.com">author:无很</a></h2>-->
    </div>

    <div id="main">
<!-- Emacs Muse 正文发布从此开始 -->
<blockquote>
<p class="quoted">Lisp使用表来表达函数，这使得它的数据和函数有同样的记法，实际上函数就是数据，也可做为其他函数的的输入参数。</p>
</blockquote>

<blockquote>
<p class="quoted">Eval函数是Lisp的核心，它的功能就是对表达式求值并得到结果。</p>
</blockquote>

<h4>EVAL</h4>

<ol>
<li><strong><em>求值规则</em></strong></li>
</ol>

<ul>
<li><strong>数值、T、NIL求值规则：数值、T和NIL是自我求值的。</strong></li>
</ul>

<p>如：
<blockquote>
<p class="quoted"></p>
</blockquote> 

<pre class="example">
       (5)   =&gt;5
       (t)   =&gt;T
       (nil) =&gt; NIL
   
</pre>

<ul>
<li><strong><em>表求值规则：将第一个元素做为被调用函数，余下的元素做为参数，首先对各参数分别求值，然后将函数作用于参数列表</em></strong></li>
</ul>

<p>如：
<blockquote>
<p class="quoted"></p>
</blockquote> 

<pre class="example">
      (* 1 2 3)                     =&gt; 6
      (numberp (+ (* 2 3) (/ 6 2))) =&gt; T
   
</pre>
表求值嵌套结构时是由内到外的求值顺序，递归进行。Lisp还有一些规则，如表的第一个元素是quote时不求值。实际上是遇到宏时不求值。例如quote宏、defun宏等。当定义的函数用于表达式时，EVAL将自动调用它进行求值。

<ul>
<li><strong>符号求值规则：求值的结果是它所指向的变量的值</strong></li>
</ul>

<p>没有赋值的变量不能求值。
<blockquote>
<p class="quoted"></p>
</blockquote> 

<pre class="example">
  (setf a 5) =&gt;A
  a          =&gt;5
  b          =&gt;&quot;error:unbound variable B&quot;
  
</pre>

<ul>
<li><strong><em>引号(quote)求值规则：引号对象求值为对象本身，相当于去掉引号</em></strong></li>
</ul>

<blockquote>
<p class="quoted"></p>
</blockquote> 

<pre class="example">
  (+ 1 2 3)  =&gt; 6
  '(+ 1 2 3) =&gt; (+ 1 2 3)
  
</pre>


<h4>Eval与Apply</h4>

<p class="first">Eval和Apply都是Lisp的原始函数，对Lisp对象，每执行一次Eval就求一次值。如下所示：</p>

 

<pre class="example">
  '(* 3 5)                             =&gt; (* 3 5)
  (eval '(* 3 5))                      =&gt; 15
  ''hello                              =&gt; 'hello
  (eval ''hello)                       =&gt; HELLO
  (eval (eval '''hello))               =&gt; HELLO
  '(list '+ '(* 2 3) 5)                =&gt;(list '+ '(* 2 3) 5)
  (eval '(list '+ '(* 2 3) 5))         =&gt; (+ (* 2 3) 5)
  (eval (eval '(list '+ '(* 2 3) 5)))  =&gt; 11
 
</pre>
用户在程序中不会明确使用eval函数，但用户程序总是在隐含使用。
函数Apply需要两从参数：一个是函数，一个是参数列表。将第二个参数作为第一个函数的参数输入，求得的值做为apply的返回值。函数参数为#'加函数名。例如：
 

<pre class="example">
  (apply #'+ '(1 2 3 4))             =&gt; 10
  (apply #'(lambda(x) (&gt; x 3) '(4))  =&gt; T
 
</pre>



<h4>赋值</h4>

<p class="first">Lisp中的赋值语句主要有setf宏、let宏等。setf在给变量赋值时，如果变量不存在则创建一个全局变量，并赋予新值,如果存在就将新值赋予变量。
<blockquote>
<p class="quoted"></p>
</blockquote> 

<pre class="example">
   &gt;(let ((a 1))
         (print a)
         (setf a 2)
         (print a)
         (setf b 3))
   &gt; 1
     2
     3
   &gt; b
     3
   &gt; a
     &quot;unbound variable A&quot;
  
</pre>


<h4>incf宏与decf宏</h4>

<p class="first">incf宏与decf宏是对变量进行自加或自减操作的宏。默认是1。如：</p>
 

<pre class="example">
  (setf a 4)               =&gt; 4
  (incf a)                 =&gt; 5
  (incf a 3)               =&gt; 8
  (decf a)                 =&gt; 7
  (decf a 2)               =&gt; 5
 
</pre>


<h4>push宏与pop宏</h4>

<p class="first">push与pop是对栈的操作，push是压栈，pop是出栈。pop具有破坏性。</p>


<h4>修改局部变量</h4>

<p class="first">定义局部变量使用let宏，变量只在let作用域内有效。格式如下：
<blockquote>
<p class="quoted"></p>
</blockquote> 

<pre class="example">
   (let ((var1 val1) (var2 val2) (varn valn))
        (表达式体))

   (let* ((var1 exp1) (var2 exp2) (varn expn))
          (表达式体))
  
</pre>
以上是let宏和let*宏的定义格式，要说明的是用let定义变量时依次对var1、var2、varn时行初始化赋值为val1、val2、valn，表达式体的最后一个表达式做为let的返回值。在用let*定义变量时，和let一样依次对var1、var2、varn进行初始化赋值，不同的是对var2赋值的exp2中可以使用var1的值，表达式体的最后一个表达式的值做为let*的返回值。


<h4>广义变量</h4>

<p class="first">广义变量是指某种数据结构的某个分量，setf、incf、decf都可以修改广义变量的值。
<blockquote>
<p class="quoted"></p>
</blockquote> 

<pre class="example">
   (setf a '(a b c))          =&gt; (a b c)
   (setf (car a) 1)           =&gt; 1
   a                          =&gt; (1 b c)
   (incf (first a))           =&gt; 2
   a                          =&gt; (2 b c)
  
</pre>
可以在广义变量中应用setf宏来操作指针。如想把三个节点的链表变成二个节点，代码如下：
<blockquote>
<p class="quoted"></p>
</blockquote> 

<pre class="example">
   (setf x '(before long ago))    =&gt; (before long ago)
   (setf b (cdr x))               =&gt; (logn ago)
   (setf (cdr x) (cddr x))        =&gt; (ago)
   x                              =&gt; (before ago)
  
</pre>
直接修改链表节点指针在大程序中很有用，改变指针要比重建链表更快，也减少了内在的需求。


<h4>链表上的破坏性操作</h4>

<p class="first">破坏性操作就是改变节点值的操作，一般以N开头命名。</p>

<ul>
<li>NCONC
nconc是一个破坏性的append宏，append是建立一个新的链表，nconc则是改变它的第一个参数的最后指针，使其指向第二个参数。如果第一个参数是nil将返回第二个参数。nconc可以接受多个参数，除最后一个参数外，其他参数（除非是nil）都将被破坏。例如：</li>
</ul>
 

<pre class="example">
 ;两个参数
 (setf x '(1 2 3))           =&gt;(1 2 3)
 (setf y '(a b c))           =&gt;(a b c)
 (append x y)                =&gt;(1 2 3 a b c)
 x                           =&gt;(1 2 3)  ;;x无变化
 y                           =&gt;(a b c)  ;;y无变化
 (nconc x y)                 =&gt;(1 2 3 a b c)
 x                           =&gt;(1 2 3 a b c) ;;修改了x的最后元素的指针
 y                           =&gt;(a b c)
 ;多个参数
 (setf x '(1 2 3) p nil y '(a b c) z '(w s q))
 (nconc(x p y z))                               =&gt;(1 2 3 a b c w s q)
 x                                              =&gt;(1 2 3 a b c w s q)
 p                                              =&gt;nil
 y                                              =&gt;(a b c w s q)
 z                                              =&gt;(w s q)
 
</pre>


<!-- Emacs Muse 正文发布至此结束 -->
</div>

<div id="footer">
 <!-- <div id="visitcounter">
   您是第 <img border="0" src="http://cb.amazingcounters.com/counter.php?i=2660657&c=7982284" alt="统计"> 位访问者
  </div> -->
  <div id="copyright">
   &copy; 2012 <a href="http://www.lisperblog.com">JOY工作室</a>
  </div>
  <div id="footdate">
    最后更新 ： 2012-02-29
  </div>
  <!--<script language="javascript" type="text/javascript" src="http://js.users.51.la/3151082.js"></script>
  <noscript><a href="http://www.51.la/?3151082" target="_blank"><img alt="51统计" src="http://ylinux.org/ylinux/media/default/ylinux/images/51.gif"/></a></noscript> -->
</div>

</body>
</html>
